from fastapi import FastAPI, Depends, HTTPException, Request
from sqlalchemy.ext.asyncio import AsyncSession
from sqlalchemy import select
from typing import List, AsyncGenerator
from contextlib import asynccontextmanager
from starlette.middleware.cors import CORSMiddleware
import hashlib
import re
from sqlalchemy.exc import IntegrityError

# 使用绝对导入
import api_tools.models as models
import api_tools.schemas as schemas
from api_tools.database import engine, AsyncSessionLocal
from api_tools.resid_client import redis_client


@asynccontextmanager
async def lifespan(app: FastAPI):
    """
    应用生命周期管理器

    在应用启动时创建数据库表，在应用关闭时清理资源
    """
    # 启动时执行的代码
    async with engine.begin() as conn:
        await conn.run_sync(models.Base.metadata.create_all)

    print("Application startup completed")

    # 应用运行期间
    yield

    # 关闭时执行的代码
    await engine.dispose()  # 清理数据库引擎连接池
    print("Application shutdown completed")


app = FastAPI(lifespan=lifespan,root_path="/api")

# 添加 CORS 中间件 - 关键修改
app.add_middleware(
    CORSMiddleware,
    allow_origins=["*"],  # 指定允许的域名
    allow_credentials=True,
    allow_methods=["*"],
    allow_headers=["*"],
)


async def get_async_db() -> AsyncGenerator[AsyncSession, None]:
    async with AsyncSessionLocal() as session:
        yield session

def hash_password(password):
    return hashlib.sha256(password.encode()).hexdigest()


# 验证用户名格式
def validate_username(username):
    return re.match(r'^[a-zA-Z0-9_]{3,20}$', username) is not None


# 验证密码强度
def validate_password(password):
    return len(password) >= 6

@app.post("/login")
async def login(user: schemas.UserLogin, db: AsyncSession = Depends(get_async_db)):
    # 查询用户
    result = await db.execute(select(models.User).where(models.User.username == user.username))
    db_user = result.scalar_one_or_none()
    if not db_user:
        raise HTTPException(status_code=400, detail="用户不存在")
    if db_user.password != hash_password(user.password):
        raise HTTPException(status_code=400, detail="密码错误")
    # 登录成功，返回用户id和用户名
    return {"user_id": db_user.id, "username": db_user.username}

@app.post("/register")
async def register(user: schemas.UserRegister, db: AsyncSession = Depends(get_async_db)):
    if not validate_username(user.username):
        raise HTTPException(status_code=400, detail="用户名格式不正确")
    if not validate_password(user.password):
        raise HTTPException(status_code=400, detail="密码长度至少6位")
    # 检查用户名是否已存在
    result = await db.execute(select(models.User).where(models.User.username == user.username))
    if result.scalar_one_or_none():
        raise HTTPException(status_code=400, detail="用户名已存在")
    db_user = models.User(username=user.username, password=hash_password(user.password))
    db.add(db_user)
    try:
        await db.commit()
        await db.refresh(db_user)
    except IntegrityError:
        await db.rollback()
        raise HTTPException(status_code=400, detail="注册失败，用户名可能已存在")
    return {"user_id": db_user.id, "username": db_user.username}


@app.post("/v1/notes", response_model=schemas.NoteCreate, status_code=201)
async def create_note(note: schemas.NoteCreate, db: AsyncSession = Depends(get_async_db)):
    db_note = models.Note(**note.dict())
    db.add(db_note)
    await db.commit()
    await db.refresh(db_note)
    return db_note


@app.get("/v1/notes", response_model=List[schemas.Note])
async def read_notes(user_id: int, skip: int = 0, limit: int = 100, db: AsyncSession = Depends(get_async_db)):
    result = await db.execute(select(models.Note).where(models.Note.user_id == user_id).offset(skip).limit(limit))
    notes = result.scalars().all()
    return notes


@app.get("/v1/notes/{note_id}", response_model=schemas.Note)
async def read_note(note_id: int , db: AsyncSession = Depends(get_async_db)):
    result = await db.execute(select(models.Note).where(models.Note.id == note_id))
    note = result.scalar_one_or_none()
    if note is None:
        raise HTTPException(status_code=404, detail="Note not found")
    return note


@app.put("/v1/notes/{note_id}", response_model=schemas.Note)
async def update_note(note_id: int, note: schemas.NoteUpdate, db: AsyncSession = Depends(get_async_db)):
    # 直接查询笔记而不是调用read_note函数
    result = await db.execute(select(models.Note).where(models.Note.id == note_id))
    db_note = result.scalar_one_or_none()
    if db_note is None:
        raise HTTPException(status_code=404, detail="Note not found")

    update_data = note.dict(exclude_unset=True)
    for key, value in update_data.items():
        setattr(db_note, key, value)

    db.add(db_note)
    await db.commit()
    await db.refresh(db_note)
    return db_note


@app.delete("/v1/notes/{note_id}", status_code=204)
async def delete_note(note_id: int, db: AsyncSession = Depends(get_async_db)):
    # 直接查询笔记而不是调用read_note函数
    result = await db.execute(select(models.Note).where(models.Note.id == note_id))
    db_note = result.scalar_one_or_none()
    if db_note is None:
        raise HTTPException(status_code=404, detail="Note not found")

    await db.delete(db_note)
    await db.commit()
    return None


@app.post("/v1/cache_test")
async def cache_test(key:str,value:str):
    """
    测试redis缓存
    """
    # 使用 SET 命令将键值对存入 Redis，并设置 60 秒的过期时间
    await redis_client.set(key, value, ex=60)
    return {"message": f"Key '{key}' set in Redis."}

@app.get("/v1/cache_test/{key}")
async def get_cache_test(key:str):
    """
    测试redis缓存
    """
    # 使用 GET 命令从 Redis 中获取键对应的值
    value = await redis_client.get(key)
    if value:
        return {"Key": key,"value_from_cache": value}
    else:
        raise HTTPException(status_code=404, detail="Key not found in Redis.")